package com.pactera.asmp.server.common.websocket;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.pactera.asmp.server.common.lock.Lock;
import com.pactera.asmp.server.common.utils.SpringUtil;
import com.pactera.asmp.server.pojo.WSMessage;
import com.pactera.asmp.server.service.IDeviceServcie;
import com.pactera.asmp.server.vo.MsgEntity;
import org.apache.shiro.session.mgt.SessionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/ws/user/aux/v1/{id}")
@Component
public class WebSocketServerUserAux {
    private Logger logger = LoggerFactory.getLogger(WebSocketServerUserAux.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private Lock lock;
    private SocketUtil socketUtil;
    private StringRedisTemplate stringRedisTemplate;
    private IDeviceServcie deviceServcie;

    public static volatile ConcurrentHashMap<String, Session> id2sessionMap = new ConcurrentHashMap<>();
    public static volatile ConcurrentHashMap<Session, String> session2idMap = new ConcurrentHashMap<>();
    public static ConcurrentHashMap<String , Session> auxMap = new ConcurrentHashMap();
    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id) throws IOException {
        String sessionKey = socketUtil().formatSessionId(id,session);
        if(StringUtils.isEmpty(sessionKey)){
            session.close();
            return;
        }
        sessionKey = "aux"+ sessionKey;
        lockService().lock(id);
        try {
            logger.info("User onOpen =================================================================================================================={}", sessionKey);
            session.setMaxIdleTimeout(15000);  // 15秒超时过期
            // 1. 本地缓存
            id2sessionMap.put(sessionKey, session);
            session2idMap.put(session, sessionKey);
            // 2. 分布式Session服务注册
            socketUtil().registerSocketServer(sessionKey);
            // 3. 心跳信息
            socketUtil().heartBeatById(sessionKey);
            // 4. 成功连接反馈
            socketUtil().openSuccessMessage(session, id,sessionKey);

            String duid = stringRedisTemplate().opsForValue().get(id.split("@")[0]+"aux");
            WebSocketServerUserAux.auxMap.put(duid, session);
        }
        finally {
            lockService().unlock(id);
        }
    }


    @OnClose
    public void onClose(Session session) {
        if (session2idMap.containsKey(session)) {
            String id = session2idMap.get(session);
            String userid = id.split("@")[0].substring(3);
            lockService().lock(userid);
            try {
                logger.info("User onClose =================================================================================================================={}", id);
                id2sessionMap.remove(id);
                session2idMap.remove(session);
                socketUtil().offlineRedisCache(id);

                Long size;
                // 终端设备状态修改  空闲
                String duid = stringRedisTemplate().opsForValue().get(userid+"aux");
                if(!StringUtils.isEmpty(duid)) {
                    deviceServcie().updateStatus(duid, 0);
                    stringRedisTemplate().delete(duid);  // 真机一对一关联

                    WebSocketServerUserAux.auxMap.remove(duid);

                    // 发送MQ消息
                    WSMessage message = new WSMessage();
                    logger.info("User onClose 201 停止发送图片");
                    message.setMsgcode("201");
                    message.setDescribe("连接已断开");
                    socketUtil().sendMQ(id,duid, objectMapper.writeValueAsBytes(message), "json",null);
                }
                size = stringRedisTemplate().opsForSet().size(SocketUtil.REDIS_KEY_SET_SCREEN_DEVICE_SESSION + ":" + duid);

                // 解绑关联
                stringRedisTemplate().delete(userid+"aux");  // 真机一对一关联
                stringRedisTemplate().opsForHash().delete(SocketUtil.REDIS_KEY_SOCKET_SERVER_USER,id);  //  SERVER 关联
                stringRedisTemplate().opsForSet().remove(SocketUtil.REDIS_KEY_WS_USER_INSTANCE+":aux"+userid,id); // 用户ID和实例ID的关联

                if(size == null || size == 0){ // 没有大屏用户绑定时，需要通知设备不再发送图片
                    // 发送MQ消息
                    WSMessage message = new WSMessage();
                    logger.info("User onClose 901 停止发送图片");
                    message.setMsgcode(WebSocketServerScreen.SCREEN_CODE_DISCONNECTED_IMAGE);
                    message.setDescribe("连接已断开");
                    socketUtil().sendMQ(id,duid, objectMapper.writeValueAsBytes(message), "json",null);
                }
            } catch (Exception e) {
                logger.info(e.getMessage());
            } finally {
                lockService().unlock(userid);
            }
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        if(!StringUtils.isEmpty(message) && message.startsWith("ping#")){
            String id = session2idMap.get(session);
            message = "ping#"+id;
            socketUtil().heartBeatByMsg(message);
            return;
        }
        String id = session2idMap.get(session);
        String userId = id.split("@")[0].substring(3);
        String duid = stringRedisTemplate().opsForValue().get(userId+"aux");
        logger.info("message:"+ message);
        if(!StringUtils.isEmpty(duid)) {
            try {
                socketUtil().sendMQ(id, duid, message.getBytes("utf-8"), "json", null);
            } catch (UnsupportedEncodingException e) {
                logger.info(e.getMessage());
            }
        }
    }

    @OnMessage
    public void onMessage(byte[] bytes, Session session) {

    }

    @OnError
    public void onError(Session session, Throwable error) {
        String id = session2idMap.get(session);
        logger.error("设备连接错误:{},原因:{}", id, error.getMessage());
    }

    private Lock lockService(){
        if(this.lock == null) this.lock =  SpringUtil.getBean(Lock.class);
        return this.lock;
    }
    private SocketUtil socketUtil(){
        if(this.socketUtil == null) this.socketUtil =  SpringUtil.getBean(SocketUtil.class);
        return this.socketUtil;
    }
    private StringRedisTemplate stringRedisTemplate(){
        if(this.stringRedisTemplate == null) this.stringRedisTemplate =  SpringUtil.getBean(StringRedisTemplate.class);
        return this.stringRedisTemplate;
    }
    private IDeviceServcie deviceServcie(){
        if(this.deviceServcie == null) this.deviceServcie =  SpringUtil.getBean(IDeviceServcie.class);
        return this.deviceServcie;
    }

}

